package com.xishu.controller;

import com.xishu.annotation.valid.Admin;
import com.xishu.annotation.valid.MethodValidator;
import com.xishu.bo.CloseReport;
import com.xishu.bo.RequestBo;
import com.xishu.bo.response.DeviceStatus;
import com.xishu.client.EmailClient;
import com.xishu.client.PrinterClient;
import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.dao.jpa.CatalogItemMappingDao;
import com.xishu.dao.jpa.ItemDao;
import com.xishu.entity.Company;
import com.xishu.entity.User;
import com.xishu.entity.config.BackConfig;
import com.xishu.entity.feedback.Feedback;
import com.xishu.entity.hardware.Printer;
import com.xishu.entity.hardware.PrinterFailedResult;
import com.xishu.entity.hardware.PrinterLabel;
import com.xishu.entity.order.Order;
import com.xishu.entity.plat.UserCompanyMapping;
import com.xishu.entity.plat.UserShopMapping;
import com.xishu.entity.shop.*;
import com.xishu.foodpanda.FoodPandaConfigService;
import com.xishu.response.ResponseData;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.*;
import com.xishu.service.close.shop.CloseShopService;
import com.xishu.service.print.PrinterService;
import com.xishu.socket.WebSocketService;
import com.xishu.util.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.GeoRadiusResponse;

import java.util.*;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.PRIVILEGE;

@RestController
@Api(description = "分店管理")
public class ShopController implements Tools {
    private static Logger logger = LoggerFactory.getLogger(ShopController.class);
    private CommonService commonService = CommonService.getInstance();
    private ShopService shopService = ShopService.getInstance();

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @ApiOperation(value = "创建分店")
    @PostMapping(value = ("/user/shop"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkCompany = true, checkCompanyId = true, companyUser = true)
    public ResponseData create(@RequestBody Shop shop) throws Exception {
        VerifyUtil.verify(() -> shop.getCompanyId() != null);
        ZhEnService.getInstance().dealZhEn(shop);

        if (shop.getShopConfig() == null) {
            ShopConfig shopConfig = new ShopConfig();
            shopConfig.setLineUpQr(true);
            //排队二维码添加默认值
            shopConfig.setOpenQueueUpQRContent(true);
            shopConfig.setQueueUpQRContent_zh(Constant.QUEUE_QR_BULLETIN_ZH);
            shopConfig.setQueueUpQRContent_en(Constant.QUEUE_QR_BULLETIN_EN);
            //点餐二维码添加默认值
            shopConfig.setOpenOrderQRContent(true);
            shopConfig.setOrderQRContent_zh(Constant.ORDER_QR_BULLETIN_ZH);
            shopConfig.setOrderQRContent_en(Constant.ORDER_QR_BULLETIN_EN);
            //给打包费固定值添加默认值
            shopConfig.setPackageFeeSelect(Constant.PACKAGE_FEE_FIXED);
            shopConfig.setPackageFeeFixed(1.0);
            shop.setShopConfig(shopConfig);
        }

        //如果不是超管，那么限制分店数量
        User user = ServletUtil.getUser();
        if (!getBoolean(user.getAdmin())) {
            Long companyId = shop.getCompanyId();
            VerifyUtil.verify(() -> companyId != null);
            Company company = CompanyService.getInstance().findCompanyById(companyId);
            List<Shop> shopList = shopService.findShopListByCompanyId(companyId);

            VerifyUtil.verify(() -> shopList.size() + 1 <= getInt(company.getShopNumbers()), ResponseStatus.REACH_MAX_SHOP_NUMBER);
        }

        //创建完分店之后，需要修改分店与用户之间的关系
        return CommonService.getInstance().createObject(shop, (Shop s) -> {
            //将餐厅的ID再设置进去
            s.setShopId(s.getId());
            commonService.directSave(s);

            //有这个餐厅的权限，就会有这个分店的权限,主要是平台管理员，以及餐厅管理员
            List<UserCompanyMapping> userCompanyMappingList = MappingService.getInstance().findUserCompanyMapping(s.getCompanyId());
            //依次添加权限
            if (isNotEmpty(userCompanyMappingList)) {
                CommonService commonService = CommonService.getInstance();

                for (UserCompanyMapping userCompanyMapping : userCompanyMappingList) {
                    UserShopMapping userShopMapping = new UserShopMapping();
                    userShopMapping.setUserId(userCompanyMapping.getUserId());
                    userShopMapping.setShopId(s.getId());

                    try {
                        commonService.createObject(userShopMapping);
                    } catch (Exception e) {
                        logger.error("e", e);
                    }
                }
            }

            ShopService.getInstance().createRunModel(s.getShopId());
        }, true, null);
    }

    @ApiOperation(value = "修改分店")
    @PutMapping(value = ("/user/shop/modify"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShop = true)
    public ResponseData modify(@RequestBody Shop shop) throws Exception {
        Long shopId = shop.getId();
        VerifyUtil.verify(() -> shopId != null);

        //如果在配置stripe ak,sk,那么校验下
        shopService.checkStripeConfig(shop);

        //如果要操作分店的状态，并且状态是正在闭店，那么不允许
        if (shop.getBusinessStatus() != null) {
            Shop shopInDb = shopService.findShopById(shop.getId());
            VerifyUtil.verify(() -> getInt(shopInDb.getStatus()) == SHOP_STATUS_NORMAL, ResponseStatus.SYSTEM_BUSY);
        }

        if (getBoolean(shop.getBusinessStatus())) {
            //如果是营业中，那么添加营业时间
            shop.setOpenTime(System.currentTimeMillis());
            Shop shopInDb = shopService.findShopById(shop.getId());

            //当前修改成分店是营业，还需要分店是未营业的状态才更新营业时间
            if (!getBoolean(shopInDb.getBusinessStatus())) {
                long currentTimeMillis = System.currentTimeMillis();
                logger.info("will modify shop open time to {}", currentTimeMillis);
                shopInDb.setOpenTime(currentTimeMillis);
                commonService.save(shopInDb);
            }
        }

        //闭店操作添加闭店时间
        if (!getBoolean(shop.getBusinessStatus())) {
            Shop shopInDb = shopService.findShopById(shopId);
            long currentTimeMillis = System.currentTimeMillis();
            logger.info("will close shop and set shop close time {}", currentTimeMillis);
            shopInDb.setCloseTime(currentTimeMillis);
            commonService.save(shopInDb);
        }

        if (shop.getFoodPandaConfig() != null) {
            FoodPandaConfig foodPandaConfig = shop.getFoodPandaConfig();
            Shop shopInDb = shopService.findShopById(shop.getId());
            shopInDb = (Shop) ClassUtil.clone(shopInDb);

            boolean configCompleteInDb = getBoolean(shopInDb.getFoodPandaConfig().getConfigComplete());

            //如果修改了密码，那么密码加密处理
            if (isNotEmpty(foodPandaConfig.getPassword())) {
                foodPandaConfig.setPassword(AES.encrypt(foodPandaConfig.getPassword()));
            }

            //如果修改了密钥
            if (isNotEmpty(foodPandaConfig.getTokenSecret())) {
                foodPandaConfig.setTokenSecret(AES.encrypt(foodPandaConfig.getTokenSecret()));
            }

            //如果没有传的值，那么用数据库里面的值来处理
            ClassUtil.getInstance().copyExistValue(foodPandaConfig, shopInDb.getFoodPandaConfig());
            shop.setFoodPandaConfig(shopInDb.getFoodPandaConfig());

            boolean foodPandaConfigComplete = shopService.isFoodPandaConfigComplete(shop.getFoodPandaConfig());

            //上一次没有配置完成，这一次配置完成了，添加food panda type类型
            if (!configCompleteInDb && foodPandaConfigComplete) {
                FoodPandaConfigService.getInstance().addFoodPandaPayType(shopId);
            }

            shop.getFoodPandaConfig().setConfigComplete(foodPandaConfigComplete);
        }

        //只要修改了状态，那么做web socket通知
        if (shop.getBusinessStatus() != null) {
            WebSocketService.getInstance().sendShopStatus(shop.getId(), shop.getBusinessStatus());
        }

        //如果传了shopConfig，那么需要
        ShopConfig shopConfig = shop.getShopConfig();
        Shop shopInDb = shopService.findShopById(shopId);
        boolean businessStatusInDb = getBoolean(shopInDb.getBusinessStatus());

        if (shopConfig != null) {
            shopInDb = (Shop) ClassUtil.clone(shopInDb);
            ShopConfig shopInDbShopConfig = shopInDb.getShopConfig();
            ClassUtil.getInstance().copyExistValue(shopConfig, shopInDbShopConfig);
            shop.setShopConfig(shopInDbShopConfig);
        }

        //美国版本的闭店请求
        if (Config.getInstance().isUsVersion() && businessStatusInDb && shop.getBusinessStatus() != null && !shop.getBusinessStatus()) {
            //需要先校验依次校验小费是否足够了
            Order order = CloseShopService.getInstance().checkTips(shopId);
            if (isNotEmpty(order.getStripeErrorMsg())) {
                return commonService.createResponse(order);
            }
        }

        ResponseData responseData = CommonService.getInstance().updateObject(shop);
        String requestId = ServletUtil.getRequestId();
        //正在营业，并且闭店的时候处理闭店报告
        if (businessStatusInDb && shop.getBusinessStatus() != null && !shop.getBusinessStatus()) {
            //异步处理，考虑到并发不高，直接起线程简单处理
            new Thread(() -> {
                try {
                    //同一个requestId，方便日志跟踪
                    ThreadContext.put(Constant.REQUEST_ID, requestId);
                    boolean zh = shop.getLang() == null || equalsIgnoreCase(shop.getLang(), "zh");
                    ShopService.getInstance().closeShop(shopService.findShopById(shop.getId()), zh);
                } catch (Exception e) {
                    logger.error("e", e);
                }
            }).start();
        }

        return responseData;
    }

    @ApiOperation(value = "查询分店")
    @PutMapping(value = ("/user/shop"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData search(@RequestBody Shop shop) throws Exception {
        //根据ID来查询
        if (shop.getShopId() != null) {
            shop.setId(shop.getShopId());
            shop.setShopId(null);
        }
        return CommonService.getInstance().searchResponse(shop, (shopInDb -> {
            if (shopInDb.getShopConfig() == null) {
                shopInDb.setShopConfig(new ShopConfig());
            }

            shopService.formatShopAddress(shopInDb);
            //设置是否有配置好支付
            shopService.setPayFlag(shopInDb);

            //是否已配置好微信支付
            if (isNotEmpty(shopInDb.getQfWeixinAppId())) {
                shopInDb.setQfWeixinAppIdConfig(true);
            }

            //是否已配置好支付宝支付
            if (isNotEmpty(shopInDb.getQfAlipayAppId())) {
                shopInDb.setQfAlipayAppIdConfig(true);
            }

            //如果shopId不存在，则添加
            if (shopInDb.getShopId() == null) {
                shopInDb.setShopId(shopInDb.getId());
                try {
                    commonService.save(shopInDb);
                } catch (Exception e) {
                    logger.error("e", e);
                }
            }

            if (shopInDb.getQrCodeDisableAddNewOrderPayFirst() == null) {
                shopInDb.setQrCodeDisableAddNewOrderPayFirst(Config.getInstance().qrCodeDisableAddNewOrderPayFirst());
            }

            shopService.setShopConfigDefaultValue(shopInDb);
            shopService.setFoodPandaValue(shopInDb);

            if (isEmpty(shopInDb.getShopImg())) {
                Long companyId = shopInDb.getCompanyId();
                try {
                    Company company = commonService.findEntity(companyId, Company.class);
                    shopInDb.setShopImg(company.getCompanyLogo());
                } catch (Exception e) {
                    logger.error("e", e);
                }
            }
        }), (shopList) -> {
            Long userId = ServletUtil.getUserId();

            //B端用户直接返回，因为B端用户的过滤限制在登录的时候处理
            if (userId != null) {
                return shopList;
            }

            boolean allMatch = shopList.stream().allMatch(s -> {
                Long companyId = s.getCompanyId();
                try {
                    Company company = CompanyService.getInstance().findCompanyById(companyId);
                    return getBoolean(company.getPass());
                } catch (Exception e) {
                    logger.error("e", e);
                    return false;
                }
            });

            if (allMatch) {
                return shopList;
            } else {
                //如果已经关闭了，那么返回空数组
                return new ArrayList<>();
            }
        });
    }

    /**
     * @param shop
     * @return
     */
    @ApiOperation(value = "查询是否有未付款的订单")
    @PutMapping(value = ("/user/shop/unpaid/order"), produces = ("application/json;charset=UTF-8"))
    public ResponseData hasUnpaidOrder(@RequestBody Shop shop) throws Exception {
        Long shopId = shopService.getShopId(shop);
        boolean unpaidOrder = shopService.checkShopUnpaidOrder(shopId);
        return commonService.createResponse(unpaidOrder);
    }

    @ApiOperation(value = "删除分店")
    @MethodValidator(checkCompany = true, companyUser = true)
    @DeleteMapping(value = ("/user/shop"), produces = ("application/json;charset=UTF-8"))
    public ResponseData delete(@RequestBody Shop shop) throws Exception {
        return CommonService.getInstance().delete(shop, null, (s) -> {
            try {
                //同步删除映射关系
                Long shopId = s.getId();
                shopService.deleteEntity(shopId, Catalog.class);
                shopService.deleteEntity(shopId, Item.class);
                shopService.deleteEntity(shopId, CatalogItemMapping.class);
                shopService.deleteEntity(shopId, Scene.class);
                shopService.deleteEntity(shopId, Printer.class);
                shopService.deleteEntity(shopId, LineUp.class);
                shopService.deleteEntity(shopId, FoodTable.class);
                shopService.deleteEntity(shopId, FoodTableConfig.class);
                shopService.deleteEntity(shopId, UserShopMapping.class);
                shopService.deleteEntity(shopId, BackConfig.class);
                shopService.deleteEntity(shopId, PrinterLabel.class);
                shopService.deleteEntity(shopId, Feedback.class);

                //只删除服务员
                UserService.getInstance().deleteWater(shopId);
            } catch (Exception e) {
                logger.error("e", e);
            }
        });
    }

    @ApiOperation(value = "导入商铺的配置, srcShopId是原分店的ID,desShopId目标分店的ID,desShopId表示将要被覆盖配置的分店")
    @PutMapping(value = ("/user/shop/import/{srcShopId}/{desShopId}/{delete}"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(companyUser = true)
    public ResponseData importShopConfig(@PathVariable("srcShopId") Long srcShopId, @PathVariable("desShopId") Long desShopId, @PathVariable(value = "delete", required = true) Boolean delete) throws Exception {
        User user = ServletUtil.getUser();
        VerifyUtil.verify(() -> UserService.getInstance().hasShopPrivilege(user, srcShopId), PRIVILEGE);
        VerifyUtil.verify(() -> UserService.getInstance().hasShopPrivilege(user, desShopId), PRIVILEGE);
        ImportService importService = ImportService.getInstance();

        //先删除以前的配置
        importService.deleteCatalog(desShopId);
        importService.deleteItem(desShopId);
        importService.deleteCatalogItemMapping(desShopId);
        importService.deleteScene(desShopId);
        importService.deletePrinter(desShopId);
        importService.deleteLineUp(desShopId);
        importService.deleteFoodTable(desShopId);
        importService.deleteFoodTableConfig(desShopId);
        importService.deletePayType(desShopId);

        //导入餐桌配置
        importService.importFoodTableConfig(srcShopId, desShopId);
        //导入场景
        importService.importScene(srcShopId, desShopId);
        //导入分类
        importService.importCatalog(srcShopId, desShopId);
        //导入商品，需要删除以前的商品吗
        importService.importItem(srcShopId, desShopId);
        //导入商铺其它配置，直接覆盖
        importService.importShopConfig(srcShopId, desShopId);
        //导入打印标签
        importService.importPrintLabel(srcShopId, desShopId);
        //导入排队配置
        importService.importLineUpConfig(srcShopId, desShopId);
        //导入支付方式
        importService.importPayType(srcShopId, desShopId);

        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "产生一个新的UUID")
    @PostMapping(value = ("/shop/uuid"), produces = ("application/json;charset=UTF-8"))
    public ResponseData generateUUID() throws Exception {
        return commonService.createResponse(MongoId.generateId());
    }

    @ApiOperation(value = "设置一个UUID")
    @PostMapping(value = ("/shop/touch/device"), produces = ("application/json;charset=UTF-8"))
    public ResponseData setUuId(@RequestBody TouchDevice touchDevice) throws Exception {
        String uuid = touchDevice.getUuid();
        VerifyUtil.verify(() -> touchDevice.getShopId() != null);
        VerifyUtil.verify(() -> isNotEmpty(uuid));
        Long userId = ServletUtil.getUserId();
        Long shopId = touchDevice.getShopId();
        Shop shop = shopService.findShopById(shopId);
        Long companyId = shop.getCompanyId();
        VerifyUtil.verify(() -> companyId != null, ResponseStatus.SYSTEM_ERROR);
        Company company = CompanyService.getInstance().findCompanyById(companyId);

        //设置当前是B端服务员登录
        ServletUtil.setSessionAttribute(B_END_CACHE_REPORT_FLAG, Boolean.TRUE);

        if (!Config.getInstance().performanceTest()) {
            List<TouchDevice> onLineTouchDeviceList = CompanyService.getInstance().findOnLineTouchDeviceByCompanyId(companyId);
            int limitOnlineTouchDeviceNumber = CompanyService.getInstance().getLimitOnlineTouchDeviceNumber(company);
            logger.info("TouchDevice list is {} and limit online touch device number is {}", onLineTouchDeviceList, limitOnlineTouchDeviceNumber);

            //在线的UUID在这次的登录范围内，不用再校验了
            //也就是说只校验新的登录范围的
            //从来没有设置过，也不用检验
            if (!onLineTouchDeviceList.stream().map(p -> p.getUuid()).collect(Collectors.toList()).contains(uuid)) {
                //必须要小于，因为再加1个就超了
                VerifyUtil.verify(() -> onLineTouchDeviceList.size() < limitOnlineTouchDeviceNumber, ResponseStatus.ONE_ACCOUNT_ONLINE_TOUCH_DEVICE_MORE_THAN_LIMIT);
            } else {
                //如果包含了，那么看当前在线数是否已经超了
                VerifyUtil.verify(() -> onLineTouchDeviceList.size() <= limitOnlineTouchDeviceNumber, ResponseStatus.ONE_ACCOUNT_ONLINE_TOUCH_DEVICE_MORE_THAN_LIMIT);
            }
        }

        Optional<TouchDevice> touchDeviceOptional = searchTouchDevice(uuid, touchDevice.getShopId());
        if (touchDeviceOptional.isPresent()) {
            TouchDevice touchDeviceInDb = touchDeviceOptional.get();
            logger.info("find the uuid device {}", touchDeviceInDb);
            touchDeviceInDb.setUserId(userId);
            touchDeviceInDb.setUpdateTime(System.currentTimeMillis());
            //更新时间
            commonService.save(touchDeviceInDb);
        } else {
            logger.info("will create new touch device");
            //创建
            touchDevice.setUpdateTime(System.currentTimeMillis());
            touchDevice.setUserId(userId);
            touchDevice.setCompanyId(companyId);

            //查询所有的设备，然后添加索引
            List<TouchDevice> touchDeviceList = ShopService.getInstance().findTouchDeviceList(shopId);

            Integer index = null;
            if (isEmpty(touchDeviceList)) {
                index = 1;
            } else {
                OptionalInt max = touchDeviceList.stream().mapToInt(p -> getInt(p.getShowIndex())).max();
                index = Math.max(1, max.getAsInt()) + 1;
            }

            touchDevice.setShowIndex(index);
            commonService.createObject(touchDevice);
        }

        //缓存里面写这个设备，并且写过期时间
        ServletUtil.setSessionAttribute(Constant.TOUCH_DEVICE_REDIS_KEY, uuid);
        JRedisUtil.setKV(uuid, uuid);
        JRedisUtil.expire(uuid, Config.getInstance().touchDeviceExpireTime());

        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "查询触摸屏列表")
    @PutMapping(value = ("/shop/touch/device"), produces = ("application/json;charset=UTF-8"))
    public ResponseData listTouchDevice(@RequestBody TouchDevice touchDevice) throws Exception {
        return commonService.searchResponse(touchDevice, (t) -> {
            boolean online = shopService.isTouchDeviceOnline(t);
            t.setOnline(online);
        });
    }

    @ApiOperation(value = "删除触摸屏，如果一个触摸屏长时间不用，可以在管理系统里面删除")
    @DeleteMapping(value = ("/shop/touch/device"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShop = true)
    public ResponseData deleteTouchDevice(@RequestBody TouchDevice touchDevice) throws Exception {
        VerifyUtil.verify(() -> touchDevice.getId() != null);
        return commonService.delete(touchDevice);
    }

    /**
     * 搜索触摸屏
     *
     * @param uuid
     * @return
     */
    public Optional<TouchDevice> searchTouchDevice(String uuid, Long shopId) {
        TouchDevice touchDevice = new TouchDevice();
        touchDevice.setUuid(uuid);
        touchDevice.setShopId(shopId);
        return commonService.searchOneOptional(touchDevice);
    }

    @ApiOperation(value = "刷新设备状态, 传参数id")
    @PutMapping(value = ("/shop/device/status"), produces = ("application/json;charset=UTF-8"))
    public ResponseData freshDeviceStatus(@RequestBody Shop shop) throws Exception {
        Long shopId = shop.getId();
        VerifyUtil.verify(() -> shopId != null);
        ArrayList<DeviceStatus> deviceStatusList = new ArrayList<>();
        List<Printer> printerList = PrinterService.getInstance().flushPrinterStatus(shopId);
        List<Printer> failedPrinterList = printerList.stream().filter(p -> getInt(p.getStatus()) != Constant.PRINTER_STATUS_ONLINE).collect(Collectors.toList());
        failedPrinterList = failedPrinterList.stream().filter(p -> getBoolean(p.getOnline())).collect(Collectors.toList());

        logger.info("failed printer list size is {}", failedPrinterList.size());

        //存在状态异常的打印机
        for (Printer printer : failedPrinterList) {
            DeviceStatus deviceStatus = new DeviceStatus();
            deviceStatus.setType(Constant.DEVICE_STATUS_TYPE_PRINTER);
            deviceStatus.setUuid(printer.getId().toString());
            deviceStatus.setName(printer.getName());
            if (printer.getStatus() == Constant.PRINTER_STATUS_NO_PAPER) {
                deviceStatus.setReason(Constant.DEVICE_STATUS_REASON_PRINTER_NO_PAPER);
            } else {
                deviceStatus.setReason(Constant.DEVICE_STATUS_REASON_PRINTER_OFF_LINE);
            }

            deviceStatusList.add(deviceStatus);
        }

        List<TouchDevice> touchDeviceList = ShopService.getInstance().findTouchDeviceList(shopId);

        for (TouchDevice touchDevice : touchDeviceList) {
            boolean online = System.currentTimeMillis() - touchDevice.getUpdateTime() < Config.getInstance().touchDeviceExpireTime() * DateUtil.ONE_SECONDS;
            if (!online) {
                logger.info("SystemTime is {} , expireTime is {}, touchDevice offline {}", System.currentTimeMillis(), Config.getInstance().touchDeviceExpireTime() * DateUtil.ONE_SECONDS, touchDevice);
            }
            touchDevice.setOnline(online);
        }

        List<TouchDevice> offlineList = touchDeviceList.stream().filter(p -> !getBoolean(p.getOnline())).collect(Collectors.toList());
        logger.info("off line touch device size is {}", offlineList.size());

        //存在异常的设备
        for (TouchDevice touchDevice : offlineList) {
            DeviceStatus deviceStatus = new DeviceStatus();
            deviceStatus.setType(Constant.DEVICE_STATUS_TYPE_TOUCH_DEVICE);
            deviceStatus.setUuid(touchDevice.getUuid());
            deviceStatus.setName(touchDevice.getUuid());
            deviceStatus.setReason(Constant.DEVICE_STATUS_REASON_TOUCH_DEVICE_OFF_LINE);
            deviceStatusList.add(deviceStatus);
        }

        //添加分店的状态值
        deviceStatusList.forEach(status -> {
            status.setShopBusinessStatus(shop.getBusinessStatus());
        });

        logger.info("status list size is {}", deviceStatusList.size());

        return commonService.createResponse(deviceStatusList);
    }

    @ApiOperation(value = "查询商铺是否有售罄的菜，参数是id")
    @PutMapping(value = ("/shop/has/sale/out"), produces = ("application/json;charset=UTF-8"))
    public ResponseData hasSaleOut(@RequestBody Shop shop) throws Exception {
        Long shopId = shop.getId();
        VerifyUtil.verify(() -> shopId != null);
        List<Item> itemList = ItemService.getInstance().findItemByShopId(shop.getId());
        boolean existItemSaleOut = itemList.stream().filter(p -> getBoolean(p.getSaleOut())).findAny().isPresent();
        return commonService.createResponse(existItemSaleOut);
    }

    @ApiOperation(value = "将所有菜都取消售罄，参数是id")
    @PutMapping(value = ("/shop/clean/sale/out"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(checkShop = true)
    public ResponseData cleanSaleOut(@RequestBody Shop shop) throws Exception {
        Long shopId = shop.getId();
        VerifyUtil.verify(() -> shopId != null);
        ItemDao itemDao = BeanUtil.getBean(ItemDao.class);
        itemDao.cleanSalOut(shopId);

        CatalogItemMappingDao catalogItemMappingDao = BeanUtil.getBean(CatalogItemMappingDao.class);
        catalogItemMappingDao.cleanSalOut(shopId);


        //查询出所有的菜品，过滤套餐，并将套餐里面已经有设置成售罄的菜品恢复
        List<Item> itemList = ItemService.getInstance().findItemByShopId(shopId);
        List<Item> setMenuSaleOutList = itemList.stream().filter(p -> getBoolean(p.getSetMenu())).filter(p -> {
            //套餐里面的菜有售罄的
            return getList(p.getSetMenuOneItemList()).stream().flatMap(p1 -> getList(p1.getItemList()).stream()).filter(i -> getBoolean(i.getSaleOut())).findAny().isPresent();
        }).collect(Collectors.toList());

        for (Item item : setMenuSaleOutList) {
            getList(item.getSetMenuOneItemList()).stream().flatMap(p -> getList(p.getItemList()).stream()).forEach(p -> {
                p.setSaleOut(false);
            });

            commonService.save(item);
        }

        return ResponseData.emptyResponse();
    }

    @ApiOperation(value = "查询分菜，分钱结账开关")
    @GetMapping(value = ("/part/pay/service/open/sale/out"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData partServiceOpen() throws Exception {
        return commonService.createResponse(Config.getInstance().partServiceOpen());
    }


    @ApiOperation(value = "查询系统配置")
    @GetMapping(value = ("/system/config"), produces = ("application/json;charset=UTF-8"))
    @MethodValidator(mustLogin = false)
    public ResponseData systemConfig() throws Exception {
        Map<String, Object> map = new HashMap<>();
        Config config = Config.getInstance();
        map.put("partServiceOpen", config.partServiceOpen());
        map.put("dola.service.version", config.getDolaVersion());
        return commonService.createResponse(map);
    }

    @ApiOperation(value = "复制商家信息给分店")
    @PutMapping(value = ("/shop/clone/company"), produces = ("application/json;charset=UTF-8"))
    public ResponseData cloneCompanyInfo(@RequestBody Shop reqShop) throws Exception {
        Long companyId = reqShop.getCompanyId();
        VerifyUtil.verify(() -> companyId != null);
        Company company = CompanyService.getInstance().findCompanyById(companyId);
        Shop shop = new Shop();

        shop.setCompanyId(companyId);
        shop.setCompanyName(company.getName());
        shop.setCompanyName_zh(company.getName_zh());
        shop.setCompanyName_en(company.getName_en());

        //设置名称
        shop.setName(company.getName());
        shop.setName_zh(company.getName_zh());
        shop.setName_en(company.getName_en());

        //设置地址
        shop.setShopAddress_zh(company.getCompanyAddress());
        shop.setShopAddress_en(company.getCompanyAddress());

        //设置详细地址
        shop.setShopAddress(company.getAddressDetail());
        shop.setShopAddress_zh(company.getAddressDetail_zh());
        shop.setShopAddress_en(company.getAddressDetail_en());

        //设置电话号码
        shop.setPhone(company.getPhone1());

        //设置介绍
        shop.setShopDescription(company.getCompanyDescription());
        shop.setShopDescription_zh(company.getCompanyDescription_zh());
        shop.setShopDescription_en(company.getCompanyDescription_en());

        //拷贝餐厅图片
        shop.setBackendImg(company.getCompanyImg());

        return commonService.createObject(shop);
    }

    /**
     * 通知服务器做打印切换
     *
     * @param requestBo
     * @return
     */
    @ApiOperation(value = "通知服务器做打印切换")
    @PutMapping(value = ("/switch/printer"), produces = ("application/json;charset=UTF-8"))
    public ResponseData switchPrinter(@RequestBody RequestBo requestBo) throws Exception {
        Long printerId = requestBo.getPrinterId();
        String touchUuid = ServletUtil.getSessionAttribute(Constant.TOUCH_DEVICE_REDIS_KEY);
        logger.info("uuid {} switch to {}", touchUuid, printerId);

        List<PrinterFailedResult> printerFailedResults = PrinterService.getInstance().queryPrinterFailedResult(touchUuid);
        Printer printer = commonService.findEntity(printerId, Printer.class);

        //检查权限
        VerifyUtil.verify(() -> UserService.getInstance().hasShopPrivilege(printer.getShopId()), PRIVILEGE);

        String printerSn = AES.decrypt(printer.getPrinterSn());

        //选择的打印机是正常的时候，依次恢复打印
        PrinterClient.getInstance().flushPrinterStatus(printer);
        Printer flushPrinter = CommonService.getInstance().findEntity(printerId, Printer.class);

        if (getInt(flushPrinter.getStatus()) != PRINTER_STATUS_ONLINE) {
            for (PrinterFailedResult printerFailedResult : printerFailedResults) {
                String printInfo = PrinterClient.getInstance().printInfo(printerSn, printerFailedResult.getPrintDetailJson());
                if (PrinterClient.getInstance().isPrinterOk(printInfo)) {
                    //打印成功后将任务删除掉
                    commonService.delete(printerFailedResult, false, null);
                }
            }
        } else {
            logger.info("printer is offline");
        }

        return ResponseData.emptyResponse();
    }

    /**
     * 查询当前分店是否显示落单审核
     *
     * @param shop
     * @return
     */
    @ApiOperation(value = "判断是否显示落单审核按钮，参数是id或者shopId")
    @PutMapping(value = ("/shop/show/send/to/kitchen/audit"), produces = ("application/json;charset=UTF-8"))
    public ResponseData showSendToKitchenAudit(@RequestBody Shop shop) throws Exception {
        Long shopId = shop.getShopId();

        if (shopId == null) {
            shopId = shop.getId();
        }

        List<Scene> sceneList = SceneService.getInstance().findSceneListByShopId(shopId);
        boolean showSendToKitchenAudit = sceneList.stream().filter(scene -> getBoolean(scene.getEnable())).flatMap(scene -> scene.getRunModuleSceneList().stream())
                //后付款
                .filter(runModuleScene -> !getBoolean(runModuleScene.getPayFirst())).filter(runModuleScene -> getBoolean(runModuleScene.getSendToKitchenAuditPayAfter())).findAny().isPresent();

        return commonService.createResponse(showSendToKitchenAudit);
    }

    @ApiOperation(value = "临时测试")
    @PutMapping(value = ("/report/test"), produces = ("application/json;charset=UTF-8"))
    @Admin
    public ResponseData test(@RequestBody Shop shop) throws Exception {
        Long id = shop.getId();
        String s = shopService.generateSendCloseReport(shopService.findShopById(id), false);
        EmailClient.getInstance().sendEmail(shop.getTestEmail(), "some test", s);
        return commonService.createResponse(shop.getTestContent());
    }

    @ApiOperation(value = "获取闭店报告数据")
    @PutMapping(value = ("/shop/close/report"), produces = ("application/json;charset=UTF-8"))
    public ResponseData generateCloseReport(@RequestBody Shop shop) throws Exception {
        Long shopId = shopService.getShopId(shop);
        VerifyUtil.verify(() -> shopId != null);

        //检查权限
        VerifyUtil.verify(() -> UserService.getInstance().hasShopPrivilege(shopId), PRIVILEGE);
        CloseReport closeReport = CloseReportService.getInstance().generateCloseReport(shopId);
        return commonService.createResponse(closeReport);
    }

    @ApiOperation(value = "查看附近商店")
    @PutMapping(value = ("/shop/search/nearshop"), produces = ("application/json;charset=UTF-8"))
    public ResponseData searchNearShops(@RequestBody Shop shop) throws Exception {
        VerifyUtil.verify(() -> shop.getCompanyId() != null);
        String key = COMPANY_GEO_KEY + shop.getCompanyId();
        List<Position> positions = new ArrayList<Position>();
        // 查询出该公司所有的shop
        List<Shop> shops = shopService.findShopListByCompanyId(shop.getCompanyId());

        if (isNotEmpty(shops)) {
            batchAddShopGeo(shops);
        } else {
            logger.info("shops is null");
        }

        List<GeoRadiusResponse> results = JRedisUtil.getGeoRadius(key, shop.getLongitude(), shop.getLatitude(), 3000);

        for (GeoRadiusResponse radius : results) {
            Long shopId = Long.parseLong(radius.getMemberByString());
            Shop byShopId = shopService.findShopById(shopId);
            Position position = new Position(byShopId.getName(), radius.getDistance(), radius.getCoordinate().getLongitude(), radius.getCoordinate().getLatitude());
            positions.add(position);
        }

        return CommonService.getInstance().createResponse(positions);
    }

    /**
     * 批量添加 shop geo 数据
     *
     * @param shops
     */
    private void batchAddShopGeo(List<Shop> shops) {
        for (Shop shop : shops) {
            String key = COMPANY_GEO_KEY + shop.getCompanyId();
            int size = shops.size();
            List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(size);
            locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(), new Point(shop.getLongitude(), shop.getLatitude())));
            redisTemplate.opsForGeo().add(key, locations);
        }
    }

}

